package cn.ddiancan.xddcloud.psc.ding.service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.ddiancan.xddcloud.common.entity.CommonResult;
import cn.ddiancan.xddcloud.psc.ding.config.XddcloudDingTalkProperties;
import cn.ddiancan.xddcloud.psc.ding.domain.DepartmentInfo;
import cn.ddiancan.xddcloud.psc.ding.domain.UserInfo;
import com.aliyun.dingtalkdrive_1_0.models.ListSpacesResponse;
import com.aliyun.dingtalkdrive_1_0.models.ListSpacesResponseBody;
import com.aliyun.dingtalkrobot_1_0.Client;
import com.aliyun.dingtalkrobot_1_0.models.RobotSendDingHeaders;
import com.aliyun.dingtalkrobot_1_0.models.RobotSendDingRequest;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiDepartmentGetRequest;
import com.dingtalk.api.request.OapiDepartmentListRequest;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiUserGetDeptMemberRequest;
import com.dingtalk.api.request.OapiUserGetRequest;
import com.dingtalk.api.request.OapiUserGetuserinfoRequest;
import com.dingtalk.api.response.OapiDepartmentGetResponse;
import com.dingtalk.api.response.OapiDepartmentListResponse;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiUserGetDeptMemberResponse;
import com.dingtalk.api.response.OapiUserGetResponse;
import com.dingtalk.api.response.OapiUserGetuserinfoResponse;
import com.taobao.api.ApiException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import static cn.ddiancan.xddcloud.psc.ding.constants.XddcloudDingConstants.DING_TALK_ACCESS_TOKEN_REDIS_PREFIX;
import static cn.ddiancan.xddcloud.psc.ding.constants.XddcloudDingConstants.DING_TALK_FETCH_DEPT_ID_URL;
import static cn.ddiancan.xddcloud.psc.ding.constants.XddcloudDingConstants.DING_TALK_FETCH_DEPT_LIST_URL;
import static cn.ddiancan.xddcloud.psc.ding.constants.XddcloudDingConstants.DING_TALK_FETCH_DEPT_MEMBERS_URL;
import static cn.ddiancan.xddcloud.psc.ding.constants.XddcloudDingConstants.DING_TALK_FETCH_TOKEN_URL;
import static cn.ddiancan.xddcloud.psc.ding.constants.XddcloudDingConstants.DING_TALK_FETCH_USER_INFO_BY_USER_ID_URL;
import static cn.ddiancan.xddcloud.psc.ding.constants.XddcloudDingConstants.DING_TALK_USER_INFO_BY_CODE;

/**
 * 钉钉工具类
 *
 * @author Zeki
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
@Service
public class XddcloudDingTalkService {


    private final XddcloudDingTalkProperties xddcloudDingTalkProperties;

    private final StringRedisTemplate redisTemplate;

    /**
     * 获取钉钉accessToken
     *
     * @return 钉钉accessToken
     */
    public String getDingTalkAccessToken() throws ApiException {
        // 从redis中获取access_token
        String dingTalkAccessTokenRedisKey =
            DING_TALK_ACCESS_TOKEN_REDIS_PREFIX + xddcloudDingTalkProperties.getAppKey();
        String accessToken = redisTemplate.opsForValue().get(dingTalkAccessTokenRedisKey);
        if (StringUtils.hasText(accessToken)) {
            return accessToken;
        }
        // 重新获取accessToken
        DefaultDingTalkClient client = new DefaultDingTalkClient(DING_TALK_FETCH_TOKEN_URL);
        OapiGettokenRequest request = new OapiGettokenRequest();
        request.setAppkey(xddcloudDingTalkProperties.getAppKey());
        request.setAppsecret(xddcloudDingTalkProperties.getAppSecret());
        request.setHttpMethod(HttpMethod.GET.name());
        OapiGettokenResponse response = client.execute(request);
        if (response.isSuccess()) {
            accessToken = response.getAccessToken();
            // 保存至redis
            redisTemplate.opsForValue().set(dingTalkAccessTokenRedisKey, accessToken, 7000, TimeUnit.SECONDS);
        }
        return accessToken;
    }

    public CommonResult<?> sendDingMessage(String content, List<String> dingUserIdList) throws Exception {
        RobotSendDingHeaders robotSendDingHeaders = new RobotSendDingHeaders();
        robotSendDingHeaders.xAcsDingtalkAccessToken = getDingTalkAccessToken();
        RobotSendDingRequest robotSendDingRequest = new RobotSendDingRequest();
        robotSendDingRequest.setContent(content);
        robotSendDingRequest.setRobotCode(xddcloudDingTalkProperties.getRobotCode());
        robotSendDingRequest.setRemindType(xddcloudDingTalkProperties.getRemindType());
        robotSendDingRequest.setReceiverUserIdList(dingUserIdList);
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        Client client = new Client(config);
        client.robotSendDingWithOptions(robotSendDingRequest, robotSendDingHeaders, new RuntimeOptions());
        return CommonResult.success();
    }

    /**
     * 获取部门列表
     *
     * @return 部门列表
     */
    public List<OapiDepartmentListResponse.Department> getDepartmentList() {
        DingTalkClient client = new DefaultDingTalkClient(DING_TALK_FETCH_DEPT_LIST_URL);
        OapiDepartmentListRequest request = new OapiDepartmentListRequest();
        request.setId(String.valueOf(BigDecimal.ONE.intValue()));
        request.setHttpMethod(HttpMethod.GET.name());
        request.setFetchChild(true);
        List<OapiDepartmentListResponse.Department> departmentList = new ArrayList<>();
        try {
            OapiDepartmentListResponse response = client.execute(request, getDingTalkAccessToken());
            departmentList = response.getDepartment();
        } catch (ApiException e) {
            log.error("获取部门列表失败{},", e.getMessage(), e);
        }
        return departmentList;
    }

    /**
     * 获取企业部门/员工信息
     *
     * @return 企业部门/员工信息
     */
    public List<DepartmentInfo> getOrganizeUserInfo() {
        // 获取部门列表
        List<DepartmentInfo> departmentInfoList = new ArrayList<>();
        List<OapiDepartmentListResponse.Department> departmentList = getDepartmentList();
        if (CollectionUtils.isEmpty(departmentList)) {
            return departmentInfoList;
        }
        // 获取部门下员工列表
        return departmentList.stream().map(department -> {
            DepartmentInfo departmentInfo = new DepartmentInfo();
            departmentInfo.setDepartmentId(String.valueOf(department.getId()));
            departmentInfo.setName(department.getName());
            departmentInfo.setParentDepartmentId(String.valueOf(department.getParentid()));
            // 获取部门下员工列表
            List<String> userIdList = getDepartmentMemberList(String.valueOf(department.getId()));
            if (!CollectionUtils.isEmpty(userIdList)) {
                userIdList.forEach(userId -> {
                    // 获取员工信息
                    UserInfo userInfo = getUserInfo(userId);
                    departmentInfo.getUserList().add(userInfo);
                });
            }
            return departmentInfo;
        }).collect(Collectors.toList());
    }

    /**
     * 获取部门用户userId列表
     *
     * @param departmentId 部门ID
     */
    private List<String> getDepartmentMemberList(String departmentId) {
        DingTalkClient client = new DefaultDingTalkClient(DING_TALK_FETCH_DEPT_MEMBERS_URL);
        OapiUserGetDeptMemberRequest request = new OapiUserGetDeptMemberRequest();
        request.setDeptId(departmentId);
        request.setHttpMethod(HttpMethod.GET.name());
        List<String> userIds = new ArrayList<>();
        try {
            OapiUserGetDeptMemberResponse response = client.execute(request, getDingTalkAccessToken());
            userIds = response.getUserIds();
        } catch (ApiException e) {
            log.error("获取部门用户列表失败{},", e.getMessage(), e);
        }
        return userIds;
    }

    /**
     * 获取用户信息
     *
     * @param userId 员工ID
     */
    public UserInfo getUserInfo(String userId) {
        DingTalkClient client = new DefaultDingTalkClient(DING_TALK_FETCH_USER_INFO_BY_USER_ID_URL);
        OapiUserGetRequest request = new OapiUserGetRequest();
        request.setUserid(userId);
        request.setHttpMethod(HttpMethod.GET.name());
        UserInfo userInfo = new UserInfo();
        try {
            OapiUserGetResponse response = client.execute(request, getDingTalkAccessToken());
            userInfo.setUserId(response.getUserid());
            userInfo.setUnionId(response.getUnionid());
            userInfo.setName(response.getName());
            userInfo.setPhoneNumber(response.getMobile());
            userInfo.setEmail(response.getEmail());
            userInfo.setOrgEmail(response.getOrgEmail());
            userInfo.setActive(response.getActive());
            userInfo.setAdmin(response.getIsAdmin());
            userInfo.setBoss(response.getIsBoss());
            userInfo.setAvatar(response.getAvatar());
            userInfo.setDepartmentIdList(
                response.getDepartment().stream().map(String::valueOf).collect(Collectors.toList()));
            userInfo.setPosition(response.getPosition());
            if (response.getHiredDate() != null) {
                userInfo.setHiredDate(
                    LocalDateTime.ofInstant(response.getHiredDate().toInstant(), ZoneId.systemDefault()).toLocalDate());
            }
            userInfo.setJobNumber(response.getJobnumber());
        } catch (ApiException e) {
            log.error("获取用户信息失败{},", e.getMessage(), e);
        }
        return userInfo;
    }

    /**
     * 获取钉钉部门信息
     *
     * @param departmentDingTalkId 钉钉部门ID
     * @return 部门信息
     */
    private DepartmentInfo getDepartmentInfo(String departmentDingTalkId) {
        DingTalkClient client = new DefaultDingTalkClient(DING_TALK_FETCH_DEPT_ID_URL);
        OapiDepartmentGetRequest request = new OapiDepartmentGetRequest();
        request.setId(departmentDingTalkId);
        request.setHttpMethod(HttpMethod.GET.name());
        DepartmentInfo departmentInfo = null;
        try {
            OapiDepartmentGetResponse response = client.execute(request, getDingTalkAccessToken());
            if (response.isSuccess()) {
                departmentInfo = new DepartmentInfo();
                departmentInfo.setDepartmentId(String.valueOf(response.getId()));
                departmentInfo.setName(response.getName());
                departmentInfo.setParentDepartmentId(String.valueOf(response.getParentid()));
            }
        } catch (ApiException e) {
            log.error("获取部门信息失败{},", e.getMessage(), e);
        }
        return departmentInfo;
    }

    /**
     * 获取用户信息
     *
     * @param code 钉钉code
     * @return 用户信息
     * @throws ApiException 异常
     */
    public UserInfo getUserbyCode(String code) throws ApiException {
        DingTalkClient client = new DefaultDingTalkClient(DING_TALK_USER_INFO_BY_CODE);
        OapiUserGetuserinfoRequest request = new OapiUserGetuserinfoRequest();
        request.setCode(code);
        request.setHttpMethod("GET");
        UserInfo userInfo = null;
        try {
            OapiUserGetuserinfoResponse response = client.execute(request, getDingTalkAccessToken());
            userInfo = getUserInfo(response.getUserid());
        } catch (ApiException e) {
            log.error("获取用户信息失败{},", e.getMessage(), e);
        }
        return userInfo;
    }

    /**
     * 获取钉钉空间列表
     *
     * @param unionId   钉钉用户id
     * @return 空间列表
     * @throws Exception 异常
     */
    public List<ListSpacesResponseBody.ListSpacesResponseBodySpaces> getDingSpaceList(String unionId) {
        List<ListSpacesResponseBody.ListSpacesResponseBodySpaces> spaces = new ArrayList<>();
        try {
            com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
            config.protocol = "https";
            config.regionId = "central";
            com.aliyun.dingtalkdrive_1_0.Client client = new com.aliyun.dingtalkdrive_1_0.Client(config);
            com.aliyun.dingtalkdrive_1_0.models.ListSpacesHeaders listSpacesHeaders =
                new com.aliyun.dingtalkdrive_1_0.models.ListSpacesHeaders();
            listSpacesHeaders.xAcsDingtalkAccessToken = getDingTalkAccessToken();
            com.aliyun.dingtalkdrive_1_0.models.ListSpacesRequest listSpacesRequest =
                new com.aliyun.dingtalkdrive_1_0.models.ListSpacesRequest().setSpaceType("org").setMaxResults(10)
                    .setUnionId(unionId);
            ListSpacesResponse listSpacesResponse =
                client.listSpacesWithOptions(listSpacesRequest, listSpacesHeaders, new RuntimeOptions());
            ListSpacesResponseBody spacesResponseBody = listSpacesResponse.getBody();
            String nextToken = spacesResponseBody.getNextToken();
            spaces.addAll(spacesResponseBody.getSpaces());
            while(StringUtils.hasText(nextToken)){
                listSpacesRequest.setNextToken(nextToken);
                ListSpacesResponse listSpacesResponseCycle =
                    client.listSpacesWithOptions(listSpacesRequest, listSpacesHeaders, new RuntimeOptions());
                ListSpacesResponseBody spacesResponseBodyCycle = listSpacesResponseCycle.getBody();
                 nextToken = spacesResponseBodyCycle.getNextToken();
                 spaces.addAll(spacesResponseBodyCycle.getSpaces());
            }
        } catch (Exception exception) {
            log.error("获取钉盘空间列表失败{}", exception.getMessage(), exception);
        }
        return spaces;
    }
}
